React RouterのpropsをTypeScriptで型付けする
こんにちは、CX事業本部 IoT事業部の若槻です。
前回のエントリではReact Routerでprops.history
を使用したルーティングを実装しました。
今回は、このReact RouterのpropsをTypeScriptで型付けしてみました。
やってみた
react-router-domのRouteComponentProps
を使用すれば、propsを型付け可能です。
propsによる値の受け渡しがない場合
まず、コンポーネント間でpropsによる値の受け渡しがない場合。
RouteComponentProps
をReact.FC
にジェネリックで渡すようにすればOKです。
import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; const Home: React.FC<RouteComponentProps> = (props) => { return ( <div> <h1>Home page</h1> <ul> <li> <button onClick={() => { props.history.push({ pathname: '/profile', state: { sourcePage: 'Home' } }); }} > Go to my profile </button> </li> </ul> </div> ); }; export default Home;
ソースコードを見ると、React.FC
のジェネリックに指定した型は引数props
の型としてそのまま使われているためです。
type FC<P = {}> = FunctionComponent<P>; interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; propTypes?: WeakValidationMap<P> | undefined; contextTypes?: ValidationMap<any> | undefined; defaultProps?: Partial<P> | undefined; displayName?: string | undefined; }
引数に直接RouteComponentProps
を型として指定することもできますが、基本的にReact.FC
を使えば良いと思います。
import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; const Home = (props: RouteComponentProps) => { return ( <div> 中略 </div> ); }; export default Home;
propsによる値の受け渡しがある場合
propsによる値の受け渡しがある場合だと、同じようにRouteComponentProps
を直接React.FC
のジェネリックで使用するとエラーとなります。
import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; const MyProfile: React.FC<RouteComponentProps> = ( props ) => { return ( <div> <h1>This is my profile</h1> <ul> {props.location.state && ( <li> <button onClick={() => { props.history.goBack(); }} > Back {props.location.state.sourcePage} </button> </li> )} </ul> </div> ); }; export default MyProfile;
props.location.state
にsourcePage
が無いためエラーとなっています。
ここでRouteComponentProps
のソースコードを見ると、これまたジェネリックでS
、つまり第三引数を指定すれば良さげですね。
export interface RouteComponentProps< Params extends { [K in keyof Params]?: string } = {}, C extends StaticContext = StaticContext, S = H.LocationState > { history: H.History<S>; location: H.Location<S>; match: match<Params>; staticContext?: C | undefined; }
というわけで次のようにしたらエラー無く型付けできるようになります。
import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; type Props = RouteComponentProps< {}, {}, { sourcePage: string } >; const MyProfile: React.FC<Props> = (props) => { return ( <div> <h1>This is my profile</h1> <ul> {props.location.state && ( <li> <button onClick={() => { props.history.goBack(); }} > Back {props.location.state.sourcePage} </button> </li> )} </ul> </div> ); }; export default MyProfile;
デモ
ここまでのコードを使用したデモです。
参考
- TypeScriptの目玉機能「ジェネリック(Generics)」はこうなっている - Build Insider
- React Router with TypeScriptでちゃんと型をつける - Qiita
以上